/*
 * Tint Browser for Android
 * 
 * Copyright (C) 2012 - to infinity and beyond J. Devauchelle and contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

package com.seal.ui.preferences;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import android.app.AlertDialog;
import android.app.ListFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.PreferenceActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.GeolocationPermissions;
import android.webkit.ValueCallback;
import android.webkit.WebStorage;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.seal.R;
import com.seal.ui.managers.UIFactory;

public class WebsitesSettingsFragment extends ListFragment {

	private static final String EXTRA_SITE = "site";

	private SiteAdapter mAdapter = null;
	private Site mSite = null;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		View view = inflater.inflate(R.layout.websites_settings_fragment, container, false);

		Bundle args = getArguments();
		if (args != null) {
			mSite = (Site) args.getParcelable(EXTRA_SITE);
		}
		if (mSite == null) {
			// View clear = view.findViewById(R.id.clear_all_button);
			// clear.setVisibility(View.VISIBLE);
			// clear.setOnClickListener(this);
		} else {
			if (!UIFactory.isTablet(getActivity())) {
				// The current website is currently shown in tablet-type preferences activity / fragements.
				getActivity().setTitle(String.format(getString(R.string.WebsitesSettingsSiteTitle), mSite.getPrettyTitle()));
			}
		}

		return view;
	}

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);

		mAdapter = new SiteAdapter(getActivity(), R.layout.websites_settings_row);

		if (mSite != null) {
			mAdapter.mCurrentSite = mSite;
		}

		getListView().setAdapter(mAdapter);
		getListView().setOnItemClickListener(mAdapter);
	}

	@Override
	public void onResume() {
		super.onResume();
		mAdapter.askForOrigins();
	}

	private void finish() {
		PreferenceActivity activity = (PreferenceActivity) getActivity();
		if (activity != null) {
			activity.finishPreferencePanel(this, 0, null);
		}
	}

	class SiteAdapter extends ArrayAdapter<Site> implements AdapterView.OnItemClickListener {

		private int mResource;
		private LayoutInflater mInflater;
		private Bitmap mUsageEmptyIcon;
		private Bitmap mUsageLowIcon;
		private Bitmap mUsageHighIcon;
		private Bitmap mLocationAllowedIcon;
		private Bitmap mLocationDisallowedIcon;
		private Site mCurrentSite;

		public SiteAdapter(Context context, int rsc) {
			this(context, rsc, null);
		}

		public SiteAdapter(Context context, int rsc, Site site) {
			super(context, rsc);

			mResource = rsc;
			mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			mUsageEmptyIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_list_data_off);
			mUsageLowIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_list_data_small);
			mUsageHighIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_list_data_large);
			mLocationAllowedIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_gps_on_holo_dark);
			mLocationDisallowedIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_gps_denied_holo_dark);
			mCurrentSite = site;

			if (mCurrentSite == null) {
				askForOrigins();
			}
		}

		/**
		 * Adds the specified feature to the site corresponding to supplied origin in the map. Creates the site if it does not already exist.
		 */
		private void addFeatureToSite(Map<String, Site> sites, String origin, int feature) {
			Site site = null;
			if (sites.containsKey(origin)) {
				site = (Site) sites.get(origin);
			} else {
				site = new Site(origin);
				sites.put(origin, site);
			}
			site.addFeature(feature);
		}

		@SuppressWarnings("rawtypes")
		public void askForOrigins() {
			// Get the list of origins we want to display.
			// All 'HTML 5 modules' (Database, Geolocation etc) form these
			// origin strings using WebCore::SecurityOrigin::toString(), so it's
			// safe to group origins here. Note that WebCore::SecurityOrigin
			// uses 0 (which is not printed) for the port if the port is the
			// default for the protocol. Eg http://www.google.com and
			// http://www.google.com:80 both record a port of 0 and hence
			// toString() == 'http://www.google.com' for both.

			WebStorage.getInstance().getOrigins(new ValueCallback<Map>() {
				@SuppressWarnings("unchecked")
				public void onReceiveValue(Map origins) {
					Map<String, Site> sites = new HashMap<String, Site>();
					if (origins != null) {
						Iterator<String> iter = origins.keySet().iterator();
						while (iter.hasNext()) {
							addFeatureToSite(sites, iter.next(), Site.FEATURE_WEB_STORAGE);
						}
					}

					askForGeolocation(sites);
				}
			});
		}

		public void askForGeolocation(final Map<String, Site> sites) {
			GeolocationPermissions.getInstance().getOrigins(new ValueCallback<Set<String>>() {
				public void onReceiveValue(Set<String> origins) {
					if (origins != null) {
						Iterator<String> iter = origins.iterator();
						while (iter.hasNext()) {
							addFeatureToSite(sites, iter.next(), Site.FEATURE_GEOLOCATION);
						}
					}

					// populateIcons(sites);
					populateOrigins(sites);
				}
			});
		}

		public void populateOrigins(Map<String, Site> sites) {
			clear();

			// We can now simply populate our array with Site instances
			Set<Map.Entry<String, Site>> elements = sites.entrySet();
			Iterator<Map.Entry<String, Site>> entryIterator = elements.iterator();
			while (entryIterator.hasNext()) {
				Map.Entry<String, Site> entry = entryIterator.next();
				Site site = entry.getValue();
				add(site);
			}

			notifyDataSetChanged();
		}

		public String sizeValueToString(long bytes) {
			// We display the size in MB, to 1dp, rounding up to the next 0.1MB.
			// bytes should always be greater than zero.
			if (bytes <= 0) {
				Log.e("WebsitesSettingsFragment", "sizeValueToString called with non-positive value: " + bytes);
				return "0";
			}
			float megabytes = (float) bytes / (1024.0F * 1024.0F);
			int truncated = (int) Math.ceil(megabytes * 10.0F);
			float result = (float) (truncated / 10.0F);
			return String.valueOf(result);
		}

		public void setIconForUsage(ImageView usageIcon, long usageInBytes) {
			float usageInMegabytes = (float) usageInBytes / (1024.0F * 1024.0F);
			// We set the correct icon:
			// 0 < empty < 0.1MB
			// 0.1MB < low < 5MB
			// 5MB < high
			if (usageInMegabytes <= 0.1) {
				usageIcon.setImageBitmap(mUsageEmptyIcon);
			} else if (usageInMegabytes > 0.1 && usageInMegabytes <= 5) {
				usageIcon.setImageBitmap(mUsageLowIcon);
			} else if (usageInMegabytes > 5) {
				usageIcon.setImageBitmap(mUsageHighIcon);
			}
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View view;

			final TextView title;
			final TextView subtitle;

			final ImageView settingsIcon;

			final ImageView usageIcon;
			final ImageView locationIcon;
			final ImageView featureIcon;

			if (convertView == null) {
				view = mInflater.inflate(mResource, parent, false);
			} else {
				view = convertView;
			}

			title = (TextView) view.findViewById(R.id.WebsitesSettingsTitle);
			subtitle = (TextView) view.findViewById(R.id.WebsitesSettingsSubTitle);
			settingsIcon = (ImageView) view.findViewById(R.id.WebsitesSettingsIcon);
			featureIcon = (ImageView) view.findViewById(R.id.WebsitesSettingsFeatureIcon);
			usageIcon = (ImageView) view.findViewById(R.id.WebsitesSettingsUsageIcon);
			locationIcon = (ImageView) view.findViewById(R.id.WebsitesSettingsLocationIcon);

			// featureIcon.setVisibility(View.GONE);
			// usageIcon.setVisibility(View.GONE);
			// locationIcon.setVisibility(View.GONE);

			if (mCurrentSite == null) {
				Site site = getItem(position);

				title.setText(site.getPrettyTitle());

				String subtitleText = site.getPrettyOrigin();
				if (subtitleText != null) {
					title.setMaxLines(1);
					title.setSingleLine(true);
					subtitle.setVisibility(View.VISIBLE);
					subtitle.setText(subtitleText);
				} else {
					subtitle.setVisibility(View.GONE);
					title.setMaxLines(2);
					title.setSingleLine(false);
				}

				usageIcon.setVisibility(View.GONE);
				locationIcon.setVisibility(View.GONE);
				featureIcon.setVisibility(View.GONE);

				// We set the site as the view's tag,
				// so that we can get it in onItemClick()
				view.setTag(site);

				if (site.hasFeature(Site.FEATURE_WEB_STORAGE)) {
					WebStorage.getInstance().getUsageForOrigin(site.getOrigin(), new ValueCallback<Long>() {
						public void onReceiveValue(Long value) {
							if (value != null) {
								setIconForUsage(usageIcon, value.longValue());
								usageIcon.setVisibility(View.VISIBLE);
							}
						}
					});
				}

				if (site.hasFeature(Site.FEATURE_GEOLOCATION)) {
					locationIcon.setVisibility(View.VISIBLE);
					GeolocationPermissions.getInstance().getAllowed(site.getOrigin(), new ValueCallback<Boolean>() {
						public void onReceiveValue(Boolean allowed) {
							if (allowed != null) {
								if (allowed.booleanValue()) {
									locationIcon.setImageBitmap(mLocationAllowedIcon);
								} else {
									locationIcon.setImageBitmap(mLocationDisallowedIcon);
								}
							}
						}
					});
				}

			} else {

				settingsIcon.setVisibility(View.GONE);
				locationIcon.setVisibility(View.GONE);
				usageIcon.setVisibility(View.GONE);
				featureIcon.setVisibility(View.VISIBLE);

				switch (mCurrentSite.getFeatureByIndex(position)) {
				case Site.FEATURE_WEB_STORAGE:
					WebStorage.getInstance().getUsageForOrigin(mCurrentSite.getOrigin(), new ValueCallback<Long>() {
						public void onReceiveValue(Long value) {
							if (value != null) {
								String usage = String.format(getString(R.string.WebsitesSettingsDataUsage), sizeValueToString(value.longValue()));
								title.setText(usage);
								subtitle.setText(R.string.WebsitesSettingsDataClickToClear);
								subtitle.setVisibility(View.VISIBLE);
								setIconForUsage(featureIcon, value.longValue());
							}
						}
					});
					break;
				case Site.FEATURE_GEOLOCATION:
					subtitle.setText(R.string.WebsitesSettingsGeolocationClickToClear);
					GeolocationPermissions.getInstance().getAllowed(mCurrentSite.getOrigin(), new ValueCallback<Boolean>() {
						public void onReceiveValue(Boolean allowed) {
							if (allowed != null) {
								if (allowed.booleanValue()) {
									title.setText(R.string.WebsitesSettingsGeolocationAllowed);
									featureIcon.setImageBitmap(mLocationAllowedIcon);
								} else {
									title.setText(R.string.WebsitesSettingsGeolocationNotAllowed);
									featureIcon.setImageBitmap(mLocationDisallowedIcon);
								}

								subtitle.setVisibility(View.VISIBLE);
							}
						}
					});
					break;
				}
			}

			return view;
		}

		public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
			if (mCurrentSite != null) {
				switch (mCurrentSite.getFeatureByIndex(position)) {
				case Site.FEATURE_WEB_STORAGE:
					new AlertDialog.Builder(getContext()).setTitle(R.string.WebsitesSettingsCleatDataDialogTitle)
							.setMessage(R.string.WebsitesSettingsCleatDataDialogMessage).setPositiveButton(R.string.OK, new AlertDialog.OnClickListener() {
								public void onClick(DialogInterface dlg, int which) {
									WebStorage.getInstance().deleteOrigin(mCurrentSite.getOrigin());
									// If this site has no more features, then go back to the
									// origins list.
									mCurrentSite.removeFeature(Site.FEATURE_WEB_STORAGE);
									if (mCurrentSite.getFeatureCount() == 0) {
										finish();
									}
									askForOrigins();
									notifyDataSetChanged();
								}
							}).setNegativeButton(R.string.Cancel, null).setIcon(android.R.drawable.ic_dialog_alert).show();
					break;
				case Site.FEATURE_GEOLOCATION:
					new AlertDialog.Builder(getContext()).setTitle(R.string.WebsitesSettingsGeolocationPageDialogTitle)
							.setMessage(R.string.WebsitesSettingsGeolocationPageDialogMessage)
							.setPositiveButton(R.string.OK, new AlertDialog.OnClickListener() {
								public void onClick(DialogInterface dlg, int which) {
									GeolocationPermissions.getInstance().clear(mCurrentSite.getOrigin());
									mCurrentSite.removeFeature(Site.FEATURE_GEOLOCATION);
									if (mCurrentSite.getFeatureCount() == 0) {
										finish();
									}
									askForOrigins();
									notifyDataSetChanged();
								}
							}).setNegativeButton(R.string.Cancel, null).setIcon(android.R.drawable.ic_dialog_alert).show();
					break;
				}
			} else {
				Site site = (Site) view.getTag();
				PreferenceActivity activity = (PreferenceActivity) getActivity();
				if (activity != null) {
					Bundle args = new Bundle();
					args.putParcelable(EXTRA_SITE, site);
					activity.startPreferencePanel(WebsitesSettingsFragment.class.getName(), args, 0, site.getPrettyTitle(), null, 0);
				}
			}
		}

		public int getCount() {
			if (mCurrentSite == null) {
				return super.getCount();
			}
			return mCurrentSite.getFeatureCount();
		}
	}

	static class Site implements Parcelable {
		private String mOrigin;
		private String mTitle;
		private Bitmap mIcon;
		private int mFeatures;

		// These constants provide the set of features that a site may support
		// They must be consecutive. To add a new feature, add a new FEATURE_XXX
		// variable with value equal to the current value of FEATURE_COUNT, then
		// increment FEATURE_COUNT.
		private final static int FEATURE_WEB_STORAGE = 0;
		private final static int FEATURE_GEOLOCATION = 1;
		// The number of features available.
		private final static int FEATURE_COUNT = 2;

		public Site(String origin) {
			mOrigin = origin;
			mTitle = null;
			mIcon = null;
			mFeatures = 0;
		}

		public void addFeature(int feature) {
			mFeatures |= (1 << feature);
		}

		public void removeFeature(int feature) {
			mFeatures &= ~(1 << feature);
		}

		public boolean hasFeature(int feature) {
			return (mFeatures & (1 << feature)) != 0;
		}

		/**
		 * Gets the number of features supported by this site.
		 */
		public int getFeatureCount() {
			int count = 0;
			for (int i = 0; i < FEATURE_COUNT; ++i) {
				count += hasFeature(i) ? 1 : 0;
			}
			return count;
		}

		/**
		 * Gets the ID of the nth (zero-based) feature supported by this site. The return value is a feature ID - one of the FEATURE_XXX values. This is required to determine which feature is displayed at a given position in the list of features for this site. This is used both when populating the view and when responding to clicks on the list.
		 */
		public int getFeatureByIndex(int n) {
			int j = -1;
			for (int i = 0; i < FEATURE_COUNT; ++i) {
				j += hasFeature(i) ? 1 : 0;
				if (j == n) {
					return i;
				}
			}
			return -1;
		}

		public String getOrigin() {
			return mOrigin;
		}

		public void setTitle(String title) {
			mTitle = title;
		}

		public void setIcon(Bitmap icon) {
			mIcon = icon;
		}

		public Bitmap getIcon() {
			return mIcon;
		}

		public String getPrettyOrigin() {
			return mTitle == null ? null : hideHttp(mOrigin);
		}

		public String getPrettyTitle() {
			return mTitle == null ? hideHttp(mOrigin) : mTitle;
		}

		private String hideHttp(String str) {
			Uri uri = Uri.parse(str);
			return "http".equals(uri.getScheme()) ? str.substring(7) : str;
		}

		@Override
		public int describeContents() {
			return 0;
		}

		@Override
		public void writeToParcel(Parcel dest, int flags) {
			dest.writeString(mOrigin);
			dest.writeString(mTitle);
			dest.writeInt(mFeatures);
			dest.writeParcelable(mIcon, flags);
		}

		private Site(Parcel in) {
			mOrigin = in.readString();
			mTitle = in.readString();
			mFeatures = in.readInt();
			mIcon = in.readParcelable(null);
		}

		public static final Parcelable.Creator<Site> CREATOR = new Parcelable.Creator<Site>() {
			public Site createFromParcel(Parcel in) {
				return new Site(in);
			}

			public Site[] newArray(int size) {
				return new Site[size];
			}
		};

	}

}
